package framework.gui.panels;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


import framework.cellularAutomaton.CellularAutomaton;
import framework.world.World;

/**
 * The panel that initializes and manages the grid the
 * user will be viewing. Also handles the events from
 * the ButtonPanel buttons.
 * 
 * @author Alex Duda
 */

@SuppressWarnings("serial")
public class GridPanel extends JPanel implements Runnable, ActionListener {

	private JFrame controllerFrame;

	private BottomButtonPanel buttonPanel;
	
	private World world;
	
	private boolean running;
	private boolean viewGrid;
	private boolean mousePressed;
	
	private int numStates;
	private int paintState;
	private int numSteps;
	private int defaultState;

	private GridCell[][] cells;
	
	/** 
	 * The grid containing all of the gridcells.
	 * @param CA
	 */
	
	public GridPanel(CellularAutomaton CA){
		controllerFrame = CA.getControllerFrame();
		world = CA.getWorld();
		viewGrid = true;
		running = false;
		defaultState = world.getRules().getDefaultState();
		paintState = defaultState;
		cells = new GridCell[world.getHeight()][world.getWidth()];
		numStates = world.getRules().getNumStates();
		
		initGrid();
	}
	
	/* Getters and Setters for GridPanel*/	
	public boolean isGridViewable(){
		return viewGrid;
	}
	
	public int getPaintState(){
		return paintState;
	}
	
	public int getNumStates(){
		return numStates;
	}
	
	public int getDefaultState(){
		return defaultState;
	}
	
	public boolean getMousePressed(){
		return mousePressed;
	}
	
	public void setMousePressed(boolean bool){
		mousePressed = bool;
	}
	
	public void setPaintState(int state) {
		paintState = state;
	}
	
	/*End of Getters and setter*/
	
	/**
	 * Initializes the grid and all of its cells
	 */
	
	public void initGrid(){
		int rows = world.getHeight();
		int cols = world.getWidth();
		
		setLayout(new GridLayout(rows, cols));
		setBorder(BorderFactory.createEmptyBorder(2,2,2,2));		

		for (int i = 0; i < rows; i++){
			for (int j = 0; j < cols; j++){
				
				cells[i][j] = new GridCell(this, world, i, j);		
				add(cells[i][j]);
				
			}
		} 
	}
	
	/**
	 * Updates all cells based on current framework.world state.
	 */
	
	public void updateCells(){
		int rows = world.getHeight();
		int cols = world.getWidth();
		
		for (int i = 0; i < rows; i++){
			for (int j = 0; j < cols; j++){
				cells[i][j].updateCell();
			}
		} 
	}
	
	/**
	 * Sets all cells to their default state
	 */
	
	public void resetCells(){
		int rows = world.getHeight();
		int cols = world.getWidth();
		
		for (int i = 0; i < rows; i++){
			for (int j = 0; j < cols; j++){
				cells[i][j].reset();
			}
		} 
	}
	
	/**
	 * Updates each cell's border
	 */
	
	public void updateGrid(){
		int rows = world.getHeight();
		int cols = world.getWidth();
		
		for (int i = 0; i < rows; i++){
			for (int j = 0; j < cols; j++){
				cells[i][j].updateBorder();
			}
		} 
	}
	
	/**
	 * Adds the pointer to the bottom button panel
	 * 
	 * @param buttons the LifeDisplays's BottomButtonPanel
	 */
	
	public void addButtonPanelReference(BottomButtonPanel buttons){
		buttonPanel = buttons;
	}
	
	/**
	 * Handles ActionEvents sent to the GridPanel by the buttons
	 * in the ButtonPanel
	 */
	
	public void actionPerformed(ActionEvent e) {
		String actionCommand = e.getActionCommand();
		
		if("start".equals(actionCommand)){
			//JOptionPane.showMessageDialog(controllerFrame, "You started life");
			buttonPanel.setStartedButtonStates();
			
			running = true;
			stepForever();
		}
		else if("stop".equals(actionCommand)){
			//JOptionPane.showMessageDialog(controllerFrame, "You stopped life");
			buttonPanel.setStoppedButtonStates();
			
			running = false;
			stop();
		}
		else if("step".equals(actionCommand)){
			//JOptionPane.showMessageDialog(controllerFrame, "You stepped life");
			
			step();
		}
		else if("toggle".equals(actionCommand)){
			viewGrid = !viewGrid;
			updateGrid();
			repaint();
		}
		else if("reset".equals(actionCommand)){
			resetCells();
			repaint();
		}
		else{
			JOptionPane.showMessageDialog(controllerFrame, "Unhandled grid event");
		}
	}

	/**
	 * This method steps the framework.world once and then updates the UI
	 * 
	 * This function is the alteration of TA Andrew Chang's code in 
	 * WorldImpl.java in hw2.
	 */
	public void step(){
		world.advanceWorldState();
		updateCells();
		repaint();
		sleep();
	}
	
	/**
	 * This method will run the framework.world forever
	 * 
	 * This function is the alteration of TA Andrew Chang's code in 
	 * WorldImpl.java in hw2.
	 */
	
	public void stepForever() {
		numSteps = -1;
		Thread t = new Thread(this);
		t.start();
	}
	
	public void stop(){
		 // value breaks concurrent loop in run()
		numSteps = 0;
	}
	
	/**
	 * This method will cause the current thread to sleep for 60 milliseconds
	 * 
	 * This function is the alteration of TA Andrew Chang's code in 
	 * WorldImpl.java in hw2.
	 */
	
	private void sleep(){
		try {
			Thread.sleep(60);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
		
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public void run() {
		if(numSteps == -1){
			while( numSteps == -1){
				step();
			}
		}	
	}
}